package com.jrsa.augmented_reality.Sun;

import java.util.ArrayList;
import java.util.Calendar;

//TODO: Encapsulate data for single time unit in object. This way
//an object for each discrete time we want to be in the path 
//(e.g. one for each hour of the day)
public class SunDataPoint
{
    Calendar cal;
    double longitude, latitude, altitude, accuracy;
    double JD, T, R, L, g;
    double daysGN, lambda, alpha, beta, delta, obliquity, obliquityCorrection;
    double localHourAngle, eccentricity, sunEqOfCtr, longPerihelion;
    double trueLong, trueAnom, appLong, varY, eqTime, trueSolarTime, solarZenithAngle;
    double elevation, azimuth;
    double gmst, gast, last;
    int JDN, dayOfYear, day, month, year, hour, minute, second;

    public SunDataPoint(int dd, int mm, int yy, int hr, int mn, int sc, double lat, double lon)
    {
        cal = Calendar.getInstance();
        day = dd;
        month = mm;
        year = yy;
        hour = hr + this.getZoneOffset(); //UTC0 time. +5 from Miami, FL 
        if(hour >= 24)
            day++;
        hour %= 24;
        minute = mn;
        second = sc;

        latitude = lat;
        longitude = lon;

        //get GPS coordinates
        latitude = 25.757448;
        longitude = -80.422197;

        calcData();
    }

    public void calcData()
    {
        //calculate Julian date info
        JDN = AstrCalc.calcJulianDay(day, month, year);
        JD = AstrCalc.calcJulianDate(JDN, hour, minute, second);
        daysGN = AstrCalc.calcNumDaysSinceGN(JD);
        T = daysGN / 36525.0; //Julian centuries since J2000
        L = AstrCalc.calcMeanLongitudeOfSun(T); // in degrees
        g = AstrCalc.calcMeanAnomalyOfSun(T); // in degrees
        eccentricity = AstrCalc.calcSunEccentricity(T);

        //some calculations to help figuring out the azimuth and elevation.
        lambda = AstrCalc.calcEclipticLongitude(L, g);
        obliquity = AstrCalc.calcObliquityOfEcliptic(T);
        obliquityCorrection = AstrCalc.calcObliquityCorrection(T, obliquity);
        R = AstrCalc.calcDistToSun(g); //in AU 

        sunEqOfCtr = AstrCalc.calcSunEqOfCtr(g, T);
        trueLong = L + sunEqOfCtr; //still degrees
        trueAnom = g + sunEqOfCtr; //still degrees
        appLong = AstrCalc.calcSunApparentLongitude(trueLong, T);

        ///get RA and Declination
        alpha = AstrCalc.calcRightAscension(obliquityCorrection, appLong);
        delta = AstrCalc.calcDeclination(obliquityCorrection, appLong);

        varY = AstrCalc.calcVarY(obliquityCorrection);
        eqTime = AstrCalc.calcEqOfTime(varY, eccentricity, L, g);

        int tz = cal.get(Calendar.ZONE_OFFSET) / 1000 / 60 / 60;

        double time = (hour + tz) / 24.0 + minute / (24.0 * 60.0) + second / (24 * 3600.0);
        trueSolarTime = AstrCalc.calcTrueSolarTime(time, eqTime, longitude, tz);
        localHourAngle = trueSolarTime / 4 < 0 ? trueSolarTime / 4 + 180 : trueSolarTime / 4 - 180;
        solarZenithAngle = AstrCalc.calcSolarZenithAngle(localHourAngle, latitude, delta);

        //final calculations for azimuth and elevation.  
        elevation = 90.0 - solarZenithAngle;
        azimuth = AstrCalc.calcAzimuth(localHourAngle, latitude, solarZenithAngle, delta);
    }
    
    public int getZoneOffset()
    {
        return Math.abs(cal.get(Calendar.ZONE_OFFSET) / 1000 / 60 / 60);
    }

    public double getAzimuth()
    {
        return azimuth;
    }

    public double getElevation()
    {
        return elevation;
    }

    public int getDay()
    {
        return day;
    }

    public int getMonth()
    {
        return month;
    }

    public int getYear()
    {
        return year;
    }

    public int getHour()
    {
        return hour;
    }

    public int getMinute()
    {
        return minute;
    }

    public int getSecond()
    {
        return second;
    }

    public static ArrayList<SunDataPoint> get24HourInterval(int sd, int sm, int sy, double lt, double lg)
    {
        ArrayList<SunDataPoint> points = new ArrayList<SunDataPoint>();
        for(int i = 0; i < 24; i++)
        {
            points.add(new SunDataPoint(sd, sm, sy, i, 0, 0, lt, lg));
        }
        return points;
    }
}